package com.ziiwee.reduce.describe.creators.fields;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.ziiwee.reduce.describe.*;
import com.ziiwee.reduce.describe.annotations.Ignore;
import com.ziiwee.reduce.describe.annotations.Label;
import com.ziiwee.reduce.describe.creators.FieldDescribeCreator;
import com.ziiwee.reduce.describe.models.FieldDescribeModel;
import com.ziiwee.reduce.describe.models.FieldOptionModel;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * @author ziiwee
 * @data 2020/8/12
 */
public abstract class AbstractFieldDescribeCreator extends AbstractDescribe implements FieldDescribeCreator, FieldDescribe {

    @Override
    public FieldDescribe create() {
        return new FieldDescribeModel(getName(), getType(), getLabel(), getDescription(), getNullable(), getReadable(), getWriteable(), getRules(), getOptions());
    }

    @Override
    public Boolean getNullable() {
        boolean notNull = getElement().getAnnotationsByType(NotNull.class).length > 0;
        boolean notBlank = getElement().getAnnotationsByType(NotBlank.class).length > 0;
        boolean notEmpty = getElement().getAnnotationsByType(NotEmpty.class).length > 0;
        return !notNull && !notBlank && !notEmpty;
    }

    @Override
    public Boolean getReadable() {
        for (JsonIgnore jsonIgnore : getElement().getAnnotationsByType(JsonIgnore.class)) {
            if (jsonIgnore.value()) {
                return false;
            }
        }
        for (Ignore ignore : getElement().getAnnotationsByType(Ignore.class)) {
            if (!ignore.readable()) {
                return false;
            }
        }
        return true;
    }

    @Override
    public Boolean getWriteable() {
        for (JsonIgnore jsonIgnore : getElement().getAnnotationsByType(JsonIgnore.class)) {
            if (jsonIgnore.value()) {
                return false;
            }
        }
        for (Ignore ignore : getElement().getAnnotationsByType(Ignore.class)) {
            if (!ignore.writeable()) {
                return false;
            }
        }
        return true;
    }

    @Override
    public List<FieldRule> getRules() {
        List<FieldRule> rules = new ArrayList<>();
        for (FieldRuleType value : FieldRuleType.values()) {
            FieldRule[] fieldRules = value.support(getElement(), getLabel());
            if (fieldRules != null) {
                rules.addAll(Arrays.asList(fieldRules));
            }
        }
        return rules;
    }

    @Override
    public List<FieldOption> getOptions() {
        List<FieldOption> options = new ArrayList<>();
        Class<?> aClass = Objects.requireNonNull(getType().resolve());
        if (aClass.isEnum()) {
            for (Field field : aClass.getDeclaredFields()) {
                if (field.isEnumConstant()) {
                    String label = null;
                    String desc = null;
                    Label[] labels = field.getAnnotationsByType(Label.class);
                    if (labels.length > 0) {
                        label = labels[0].value();
                        desc = labels[0].desc();
                    }
                    options.add(new FieldOptionModel(field.getName(), label, desc));
                }
            }
        }
        return options;
    }
}
